
import {
  codeFrameColumns,
  SourceLocation,
} from '@babel/code-frame';
import { Cache, ValueFactory } from './types';
import hashId from 'hash-sum';

export {
  hashId
}

export function hash(...valueList: any[]) {
  return hashId(valueList.map(val => String(val)).join(''));
}

/**
 * @internal
 */
export function formatError(message : string, path : string, source : string, location:object = null) : string {
  return JSON.stringify({
    message,
    path,
    source,
    location
  }, null,2);
}


/**
 * @internal
 */
export function formatErrorLineColumn(message : string, path : string, source : string, line? : number, column? : number) : string {
  if (!line) {
    return formatError(message, path, source)
  }

  const location: SourceLocation = {
    start: { line, column },
  };

  return formatError(codeFrameColumns(source, location, { message }), path, source)
  // return formatError(message, path, source, location)
}



/**
 * @internal
 */
export function formatErrorStartEnd(message : string, path : string, source : string, start : number, end? : number) : string {
  if (!start) {
    return formatError(message, path, source)
  }

  const location: SourceLocation = {
    start: { line: 1, column: start }
  };
  if (end) {
    location.end = {line: 1, column: end}
  }

  return formatError(codeFrameColumns(source, location, { message }), path, source)
  // return formatError(message, path, source, location)
}



/**
 * Simple cache helper
 * preventCache usage: non-fatal error
 * @internal
 */
export async function withCache( cacheInstance : Cache|undefined, key : any[], valueFactory : ValueFactory ) : Promise<any> {

  let cachePrevented = false;

  const api = {
    preventCache: () => cachePrevented = true,
  }

  if ( cacheInstance === undefined )
    return await valueFactory(api);

  const hashedKey = hash(...key);
  const valueStr = await cacheInstance.get(hashedKey);
  if ( valueStr !== undefined )
    return JSON.parse(valueStr);

  const value = await valueFactory(api);

  if ( cachePrevented === false )
    await cacheInstance.set(hashedKey, JSON.stringify(value));

  return value;
}


/**
 * @internal
 */
export function interopRequireDefault(obj : any) : any {

  return obj && obj.__esModule ? obj : { default: obj };
}


export function traverse(node:any, dependencies: Set<string>, callback?: (node:any)=>void){
  callback && callback(node);
  if (node.type === 'ImportDeclaration') {
    dependencies.add(node.source.value);
  }else if (node.type === 'ExportAllDeclaration'){
    dependencies.add(node.source.value);
  } else if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'require') {
    // 处理 require 调用，假设参数是一个字符串字面量
    if (node.arguments.length === 1 && (node.arguments[0].type === 'Literal' || node.arguments[0].type =='StringLiteral') && typeof node.arguments[0].value === 'string') {
      dependencies.add(node.arguments[0].value);
    }
  }
  for (let key in node) {
    if (node[key] && typeof node[key] === 'object' && node[key] !== null) {
      traverse(node[key], dependencies, callback);
    }
  }
}


export function customTraverseAst(ast:any){
  const dependencies = new Set<string>();
  let hasJsx = false;
  let hasTsx = false;
  traverse(ast, dependencies,(node)=>{
    if (node.type){
      if (['JSXElement','JSXFragment'].includes(node.type)){
        hasJsx = true;
      }
      if (['TSTypeAnnotation','TSInterfaceDeclaration','TSPropertySignature'].includes(node.type)){
        hasTsx = true;
      }
    }
  });
  return {
    depsList: Array.from(dependencies),
    hasJsx,
    hasTsx
  }
}

